#include "calculator.h"

#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;
namespace ascii = qi::ascii;

template<typename Iterator>
struct calculator : qi::grammar<Iterator, big_int(), ascii::space_type>
{   
   calculator() : calculator::base_type(start)
   {
      using qi::_1;
      using qi::_2;
      using qi::_val;
      using qi::lit;
      using namespace functions;

      big_int_str = +(ascii::digit);
      function = +(ascii::alpha);
      number = big_int_str[_val = big_int_from_str()(_1)];
      base = number[_val = _1]
              | (lit("-") >> base)[_val = -_1]
              | (lit("(") >> expression >> lit(")"))[_val = _1]
              | (function >> lit("(") >> expression >> lit(")"))[_val = user_function()(_1, _2)];
      power = base[_val = _1] >> -(lit("^") >> power)[_val = pow()(_val, _1)];
      term = power[_val = _1] >> *((lit("*") >> power)[_val *= _1] | (lit("/") >> power)[_val /= _1]);
      expression = term[_val = _1] >> *((lit("+") >> term)[_val += _1] | (lit("-") >> term)[_val -= _1]);
      start %= expression > qi::eoi;
   }

private:
   qi::rule<Iterator, big_int(), ascii::space_type> start, expression, term, power, base, number;
   qi::rule<Iterator, std::string(), ascii::space_type> big_int_str, function;
};

calculator<std::string::const_iterator> grammar;

big_int evaluate(const std::string& str)
{
   big_int result;
   
   std::string::const_iterator begin = str.begin();
   std::string::const_iterator end = str.end();
   
   qi::phrase_parse(begin, end, grammar, ascii::space, result);
   
   return result;
}